<?php
/**
 * In this file the class '\BF\FrameworkConfig' is defined.
 *
 * @author   Kado <ulfikado@gmail.com>
 * @category Beluga PHP5.3+ Framework
 * @package  BF\Core
 * @since    2015-01-17 14:29
 * @version  0.1
 */

namespace BF
{


   /**
    * include the \BF\LocaleConfig class
    */
   require_once __DIR__ . '/LocaleConfig.class.php';

   /**
    * include the \BF\TranslatorConfig class
    */
   require_once __DIR__ . '/TranslatorConfig.class.php';


   /**
    * This class defines the configuration class of the Beluge PHP5 framework.
    *
    * @property boolean              $LogErrors     Gets or sets PHPs 'log_errors' directive value.
    * @property boolean              $DisplayErrors Gets or sets PHPs 'display_errors' directive value.
    * @property \BF\LocaleConfig     $Locale        The Global locale settings
    * @property \BF\TranslatorConfig $Translator    The Global Translator settings
    * @since    v0.1
    */
   class FrameworkConfig extends \BF\ExplicitGetterSetter
   {

      # <editor-fold defaultstate="collapsed" desc=" - - -   P U B L I C   F I E L D S   - - - - - - - - - - - - - - - - - - - - - - -">

      /**
       * If you want enable, loging all E_NOTICE, E_STRICT, E_DEPRECATED, you have to set this value to TRUE.
       *
       * @var    boolean
       */
      public $LogNotices = false;

      /**
       * If you want enable, triggering all E_NOTICE, E_STRICT, E_DEPRECATED, you have to set this value to TRUE.
       *
       * @var    boolean
       */
      public $TriggerNotices = false;

      /**
       * If DEBUGING is enabled this flag means, a notice, strict error or deprecated error stops the stript
       * execution and outputs the error.
       *
       * @var boolean
       */
      public $StopOnNotice = false;

      /**
       * If you want to enable Debuging you have to set this value to TRUE.
       *
       * Only if debugging is enabled catchable errors will be shown.
       *
       * @var    boolean
       * @since  v0.1
       */
      public $DebugEnabled = false;

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - -   P R I V A T E   F I E L D S   - - - - - - - - - - - - - - - - - - - - - -">

      /**
       * @var \BF\LocaleConfig
       */
      private $_locale;

      /**
       * @var \BF\TranslatorConfig
       */
      private $_translator;

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - -   P R I V A T E   S T A T I C   F I E L D S   - - - - - - - - - - - - - - -">

      /**
       * The singleton instance of this object.
       *
       * @var    \BF\FrameworkConfig
       */
      private static $instance = null;

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - >   P R I V A T E   C O N S T R U C T O R   - - - - - - - - - - - - - - - - -">

      /**
       * Hidde the constructor, because its a singleton
       *
       * @since  v0.1
       */
      private function __construct()
      {
         $this->_locale     = new \BF\LocaleConfig();
         $this->_translator = new \BF\TranslatorConfig();
      }

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - -   P U B L I C   M E T H O D S   - - - - - - - - - - - - - - - - - - - - - -">

      /**
       * Returns the current defined Locale config.
       *
       * @return \BF\LocaleConfig
       */
      public function getLocale()
      {

         return $this->_locale;

      }

      /**
       * Returns the current defined translator config.
       *
       * @return \BF\TranslatorConfig
       */
      public function getTranslator()
      {

         return $this->_translator;

      }

      /**
       * Returns, if PHP's error logging is enabled.
       *
       * @return boolean
       */
      public function getLogErrors()
      {

         return self::isBooleanTrue( \ini_get( 'log_errors' ) );

      }

      /**
       * Returns if PHP currently is configured to display the errors.
       *
       * @return boolean
       */
      public function geDisplayErrors()
      {

         return self::isBooleanTrue( \ini_get( 'display_errors' ) );

      }

      /**
       * Changes the 'log_errors' PHP config directive to be enabled or disabled.
       *
       * @param boolean $enabled
       */
      public function setLogErrors( $enabled )
      {
         \ini_set( 'log_errors', \boolval( $enabled ) ? 'On' : 'Off' );
      }

      /**
       * Changes the 'display_errors' PHP config directive to be enabled or disabled.
       *
       * @param boolean $enabled
       */
      public function setDisplayErrors( $enabled )
      {
         \ini_set( 'display_errors', \boolval( $enabled ) ? 'On' : 'Off' );
      }

      /**
       * Sets a new Local configuration.
       *
       * @param \BF\LocaleConfig $value
       */
      public function setLocale( \BF\LocaleConfig $value = null )
      {
         if ( ! ( $value instanceof \BF\LocaleConfig ) )
         {
            // If the value is not a \BF\LocaleConfig instance set a default config
            $this->_locale = new \BF\LocaleConfig();
         }
         else
         {
            $this->_locale = $value;
         }
      }

      /**
       * Sets a new Translator configuration.
       *
       * @param \BF\TranslatorConfig $value
       */
      public function setTranslator( \BF\TranslatorConfig $value = null )
      {
         if ( ! ( $value instanceof \BF\TranslatorConfig ) )
         {
            // If the value is not a \BF\LocaleConfig instance set a default config
            $this->_translator = new \BF\TranslatorConfig();
         }
         else
         {
            $this->_translator = $value;
         }
      }

      /**
       * Returns a array with all persistable values of the current instance.
       *
       * The returned Array has the keys 'DebugEnabled', 'LogNotices', 'TriggerNotices' and
       * 'StopOnNotice', 'Locale', 'Translator'.
       *
       * @return array
       */
      public function toArray()
      {

         return array(
            'DebugEnabled'                 => $this->DebugEnabled,
            'LogNotices'                   => $this->LogNotices,
            'TriggerNotices'               => $this->TriggerNotices,
            'StopOnNotice'                 => $this->StopOnNotice,
            'Locale'                       => $this->_locale->toArray(),
            'Translator'                   => $this->_translator->toArray()
         );

      }

      /**
       * Reinits the instance data from the defined array, requires keys (caseless) are: 'DebugEnabled', 'LogNotices',
       * 'ShowNotices', 'TriggerNotices', 'UseInternalExceptionHandler', 'Locale'
       *
       * @param  array $array
       * @return int returns how many array values are used.
       */
      public function fromArray( array $array )
      {

         // Make lower case array keys
         $array = \array_change_key_case( $array );

         $handled = 0;

         if ( \array_key_exists( 'debugenabled', $array ) )
         {
            $this->DebugEnabled = self::isBooleanTrue( $array[ 'debugenabled' ] );
            $handled++;
         }
         if ( \array_key_exists( 'lognotices', $array ) )
         {
            $this->LogNotices = self::isBooleanTrue( $array[ 'lognotices' ] );
            $handled++;
         }
         if ( \array_key_exists( 'triggernotices', $array ) )
         {
            $this->TriggerNotices = self::isBooleanTrue( $array[ 'triggernotices' ] );
            $handled++;
         }
         if ( \array_key_exists( 'stoponnotice', $array ) )
         {
            $this->StopOnNotice = self::isBooleanTrue( $array[ 'stoponnotice' ] );
            $handled++;
         }
         if ( \array_key_exists( 'locale', $array ) && \is_array( $array[ 'locale' ] ) )
         {
            $handled += ( $this->_locale->fromArray( $array[ 'locale' ] ) > 0 ) ? 1 : 0;
         }
         if ( \array_key_exists( 'translator', $array ) && \is_array( $array[ 'translator' ] ) )
         {
            $handled += ( $this->_translator->fromArray( $array[ 'translator' ] ) > 0 ) ? 1 : 0;
         }

         return $handled;

      }

      /**
       * Writes all current configuration data to current session.
       *
       * @param string  $sessionVarName   The name of the sessionvariable to use (defaults to 'BfConfig')
       * @see   \BF\FrameworkConfig::InitFromSession()
       */
      public function saveToSession( $sessionVarName = 'BfConfig' )
      {
         $_SESSION[ $sessionVarName ] = $this->toArray();
      }

      /**
       * Writes all current configuration data to a XML file.
       *
       * @param  string $xmlFilePath THe path of the XML file. (Ensure to use absolute paths!)
       * @throws \BF\PhpException If $xmlFilePath is wrong/unusable
       */
      public function saveAsXmlFile( $xmlFilePath )
      {

         // Getting the path of the file containing folder
         $folder = \dirname( $xmlFilePath );

         if ( ! \is_dir( $folder ) )
         {

            // If the folders does not exist, create it
            if ( ! \mkdir( $folder, 0775, true ) )
            {

               // If folder creation fails
               throw new \BF\PhpException(
                  'Can not create the folder "'
                     . $folder
                     . '" for saving \\BF\\FrameworkConfig instance data inside a XML file!',
                  \E_USER_WARNING,
                  __LINE__ - 9, // ATTENTION: Dont change something before this code line, or you must adjust this code
                  __FILE__
               );

            }

         }

         // Create the required XmlWriter instance
         $w = new \XMLWriter();

         try { $isExecuted = $w->openUri( $xmlFilePath ); }
         catch ( \BF\PhpException $pex )
         {
            throw new \BF\PhpException(
               'Can not open XML file "'
                  . $xmlFilePath
                  . '" for saving \\BF\\FrameworkConfig instance data!'
                  . "\n  "
                  . $pex->getMessage(),
               \E_USER_ERROR,
               __LINE__ - 10, // ATTENTION: Dont change something before this code line, or you must adjust this code
               __FILE__
            );
         }

         if ( ! $isExecuted )
         {

            // If the opening of the XML file fails trigger a associated exception
            throw new \BF\PhpException(
               'Can not open XML file "'
                  . $xmlFilePath
                  . '" for saving \\BF\\FrameworkConfig instance data!',
               \E_USER_ERROR,
               __LINE__ - 24, // ATTENTION: Dont change something before this code line, or you must adjust this code
               __FILE__
            );

         }

         $w->setIndent( true );
         $w->setIndentString( '  ' );
         $w->startDocument( '1.0', 'utf-8' );
         $w->startElement( 'BfFrameworkConfig' );

         $w->writeAttribute( 'debug', $this->DebugEnabled ? '1' : '0' );

         $w->startElement( 'Notices' );
         $w->writeAttribute( 'log',     $this->LogNotices ? '1' : '0' );
         $w->writeAttribute( 'trigger', $this->TriggerNotices ? '1' : '0' );
         $w->writeAttribute( 'stop',    $this->StopOnNotice ? '1' : '0' );
         $w->endElement(); // /Notices

         $this->Locale->writeXml( $w );
         $this->Translator->writeXml( $w );

         $w->endElement(); // /BfFrameworkConfig
         $w->endDocument();
         $w->flush();

      }

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - -   P U B L I C   S T A T I C   M E T H O D S   - - - - - - - - - - - - - - -">

      /**
       * Returns the Singleton instance of the object.
       *
       * @return \BF\FrameworkConfig
       */
      public static function GetInstance()
      {

         if ( ! \is_null( self::$instance ) )
         {
            // If allready a instance exists, return it.
            return self::$instance;
         }

         self::$instance = new \BF\FrameworkConfig();

         return self::$instance;

      }

      /**
       * Reinitialize the global instance from data, stored in current session.
       *
       * @param  string  $sessionVarName Init from this session field. You can also access 2nd level session array
       *                                 Elements by using the following syntax: 'field[subfield]'
       * @param  boolean $updateLocale   Update the gloabal {@see \BF\Locale} instance with the settings.
       *                                 (default=TRUE)
       * @return boolean Returns TRUE on success, or FALSE on error.
       */
      public static function InitFromSession( $sessionVarName = 'BfConfig', $updateLocale = true )
      {

         if ( \preg_match( '~^[^\[\]]+$~i', $sessionVarName, $m ) )
         {
            if ( ! isset( $_SESSION[ $sessionVarName ] )
              || ! \is_array( $_SESSION[ $sessionVarName ] ) )
            {
               return false;
            }
            // Ensure a usable instance exists
            self::GetInstance();
            self::$instance->fromArray( $_SESSION[ $sessionVarName ] );
            // Update the global Locale-Instance by need
            if ( $updateLocale )
            {
               \BF\Locale::Reinit( self::$instance->Locale );
            }
            return true;
         }
         if ( ! \preg_match( '~^([a-z_][a-z0-9_]*)\[([^\]]*)\]$~i', $sessionVarName, $m ) )
         {
            return false;
         }
         if ( ! isset( $_SESSION[ $m[ 1 ] ] )
           || ! \is_array( $_SESSION[ $m[ 1 ] ] ) )
         {
            return false;
         }
         if ( empty( $m[ 2 ] ) )
         {
            if ( isset( $_SESSION[ $m[ 1 ] ][ 0 ] )
              && \is_array( $_SESSION[ $m[ 1 ] ][ 0 ] ) )
            {
               // Ensure a usable instance exists
               self::GetInstance();
               self::$instance->fromArray( $_SESSION[ $m[ 1 ] ][ 0 ] );
               // Update the global Locale-Instance by need
               if ( $updateLocale )
               {
                  \BF\Locale::Reinit( self::$instance->Locale );
               }
               return true;
            }
            return false;
         }
         $m2 = is_numeric( $m[ 2 ] ) ? \intval( $m[ 2 ] ) : $m[ 2 ];
         if ( isset( $_SESSION[ $m[ 1 ] ][ $m2 ] )
           && \is_array( $_SESSION[ $m[ 1 ] ][ $m2 ] ) )
         {
            self::GetInstance();
            self::$instance->fromArray( $_SESSION[ $m[ 1 ] ][ $m2 ] );
            // Update the global Locale-Instance by need
            if ( $updateLocale )
            {
               \BF\Locale::Reinit( self::$instance->Locale );
            }
            return true;
         }
         return false;

      }

      /**
       * Loads the current global instance from defined XML file and returns if it was successfull.
       *
       * @param  string  $xmlFile      The XML file that defines the FramworkConfig instance data.
       * @param  boolean $updateLocale Update the gloabal {@see \BF\Locale} instance with the settings. (defaults to TRUE)
       * @return boolean
       */
      public static function LoadFromXmlFile( $xmlFile, $updateLocale = true )
      {

         if ( ! \file_exists( $xmlFile ) )
         {
            return false;
         }

         $document = \simplexml_load_file( $xmlFile );

         $debug = \BF\XmlAttributeHelper::GetAttributeValue( $document, 'debug', '0' ) === '1';

         if ( ! isset( $document->Notices )
           || ! isset( $document->LocaleConfig )
           || ! isset( $document->TranslatorConfig ) )
         {
            return false;
         }

         // Ensure a usable instance exists
         self::GetInstance();

         self::$instance->DebugEnabled = $debug;
         self::$instance->LogNotices =
            ( \BF\XmlAttributeHelper::GetAttributeValue( $document->Notices[ 0 ], 'log', '0' ) === '1' );
         self::$instance->TriggerNotices =
            ( \BF\XmlAttributeHelper::GetAttributeValue( $document->Notices[ 0 ], 'trigger', '0' ) === '1' );
         self::$instance->StopOnNotice =
            ( \BF\XmlAttributeHelper::GetAttributeValue( $document->Notices[ 0 ], 'stop', '0' ) === '1' );
         if ( false !== ( $lconf = \BF\LocaleConfig::FromXmlElement( $document ) ) )
         {
            self::$instance->_locale = $lconf;
            // Update the global Locale-Instance by need
            if ( $updateLocale )
            {
               \BF\Locale::Reinit( self::$instance->_locale );
            }
         }
         if ( false !== ( $tconf = \BF\TranslatorConfig::FromXmlElement( $document ) ) )
         {
            self::$instance->_translator = $tconf;
         }
         return true;

      }

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - -   P R I V A T E   S T A T I C   M E T H O D S   - - - - - - - - - - - - - -">

      /**
       * @param $value
       * @return bool
       * @internal
       * @ignore
       */
      public static function isBooleanTrue( $value )
      {
         if ( \is_bool( $value ) )
         {
            return $value;
         }
         return ( bool ) \preg_match( '~^([1-9]\d*|t(rue)?|on|enabled|y(es)?)$~i', \strval( $value ) );
      }

      # </editor-fold>

   }


}

